#include "stdafx.h"
#include "FTLibOpenCL.h"
#include "clFFTImp.h"
#include "include_clfft.h"
#include "clffthandle.h"
#include "clFftHelper.h"

static clfftStatus ThrowIfErrorFftStatus(clfftStatus status, const char* str = nullptr)
{
	if (status != CLFFT_SUCCESS)
	{
		throw std::runtime_error(str != nullptr ? str : "");
	}

	return status;
}

struct Deleter_cl_mem
{
	// By defining the pointer type, we can delete a type other than T*.
	// In other words, we can declare unique_ptr<HANDLE, Deleter> instead of
	// unique_ptr<void, Deleter> which leaks the HANDLE abstraction.
	typedef cl_mem pointer;

	void operator()(cl_mem m)
	{
		if (m != NULL)
		{
			clReleaseMemObject(m);
		}
	}
};

struct Deleter_clfftPlan
{
	typedef clfftPlanHandle pointer;

	void operator()(clfftPlanHandle h)
	{
		if (h != NULL)
		{
			clfftDestroyPlan(&h);
		}
	}
};

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------

// Get device type as string  
static const char* getDevTypeString(cl_device_type type)
{
	switch (type)
	{
	case CL_DEVICE_TYPE_CPU:
		return "CPU";
		break;
	case CL_DEVICE_TYPE_GPU:
		return "GPU";
		break;
	case CL_DEVICE_TYPE_ACCELERATOR:
		return "ACCELERATOR";
		break;
	default:
		return "DEFAULT";
		break;
	}
}

//struct CL_ContextAndCommandQueue
//{
//	cl_context cl_dev_context;
//	cl_command_queue cl_queue;
//};

static CL_ContextAndCommandQueue createCLContext()
{
	cl_uint cl_platformsN = 0;
	cl_platform_id *cl_platformIDs = NULL;
	cl_uint cl_deviceCount = 0;
	cl_device_id *cl_devices = NULL;
	cl_context cl_dev_context;
	cl_int cl_err = 0;

	clGetPlatformIDs(0, NULL, &cl_platformsN);
	cl_platformIDs = (cl_platform_id*)malloc(cl_platformsN * sizeof(cl_platform_id));
	clGetPlatformIDs(cl_platformsN, cl_platformIDs, NULL);


	for (cl_uint i = 0; i < cl_platformsN; i++)
	{
		// Get number of available devices for this platform  
		clGetDeviceIDs(cl_platformIDs[i], CL_DEVICE_TYPE_ALL, NULL, NULL, &cl_deviceCount);

		// Skip platform if no device available  
		if (cl_deviceCount < 1)
			continue;

		// Get available device IDs for this platform  
		cl_devices = (cl_device_id*)malloc(cl_deviceCount * sizeof(cl_device_id));
		clGetDeviceIDs(cl_platformIDs[i], CL_DEVICE_TYPE_ALL, cl_deviceCount, cl_devices, NULL);

		// Print platform name  
		char platform_name[1024];
		clGetPlatformInfo(cl_platformIDs[i], CL_PLATFORM_NAME, 1024, &platform_name, NULL);
		printf("\nCompute using OpenCl platform #%i [ %s ]\n", i, platform_name);

		// Loop over devices  
		for (cl_uint j = 0; j < cl_deviceCount; j++)
		{
			// Print device name and type  
			cl_device_type device_type;
			char device_name[1024];
			clGetDeviceInfo(cl_devices[j], CL_DEVICE_NAME, 1024, &device_name, NULL);
			clGetDeviceInfo(cl_devices[j], CL_DEVICE_TYPE, sizeof(cl_device_type), &device_type, NULL);
			printf("\n\tUsing OpenCl device #%i [ %s -- %s ]\n", j, device_name, getDevTypeString(device_type));

			// Create OpenCL context  
			cl_context_properties cps[3] =
			{
				CL_CONTEXT_PLATFORM,
				(cl_context_properties)cl_platformIDs[i],
				0
			};

			CL_ContextAndCommandQueue ccq;
			ccq.cl_dev_context = clCreateContext(cps, cl_deviceCount, cl_devices, NULL, NULL, &cl_err);
			ccq.cl_queue = clCreateCommandQueue(ccq.cl_dev_context, cl_devices[j], CL_QUEUE_PROFILING_ENABLE, &cl_err);

			return ccq;
			//checkCL(cl_err);
		}
	}
}

OPENCLFTPLANHANDLE clFFT_CreatePlan_2dRealToComplexPackedFftw(uint32_t width, uint32_t height, float scaleFactor)
{
	auto ptrFftHelper = clFftHelper::GetInstance();
	CL_ContextAndCommandQueue contextAndCommandQueue = ptrFftHelper->CreateContext();

	//CL_ContextAndCommandQueue contextAndCommandQueue = createCLContext();

	// Setup FFT  
	/*clfftSetupData setupData; ZeroMemory(&setupData, sizeof(setupData));
	clfftStatus status;
	status = clfftInitSetupData(&setupData);
	status = clfftSetup(&setupData);*/

	cl_int cl_error;
	//cl_mem srcdata = clCreateBuffer(contextAndCommandQueue.cl_dev_context, CL_MEM_READ_WRITE, width*height*sizeof(float), NULL, &cl_error);

	uint32_t destHeight = height;
	uint32_t destWidth = width / 2 + 1;
	size_t destSize = destHeight * destWidth * sizeof(float) * 2;
	//cl_mem destdata = clCreateBuffer(contextAndCommandQueue.cl_dev_context, CL_MEM_READ_WRITE, destSize, NULL, &cl_error);


	//cl_mem inoutdata = clCreateBuffer(contextAndCommandQueue.cl_dev_context, CL_MEM_READ_WRITE, destSize, NULL, &cl_error);
	std::unique_ptr<cl_mem, Deleter_cl_mem> inoutdata(clCreateBuffer(contextAndCommandQueue.cl_dev_context, CL_MEM_READ_WRITE, destSize, NULL, &cl_error));
	if (cl_error != CL_SUCCESS)
	{
		throw std::runtime_error("clCreateBuffer");
	}
	//cl_mem inoutdata = clCreateImage2D(contextAndCommandQueue.cl_dev_context, CL_MEM_READ_WRITE, )


	clfftPlanHandle planHandleTmp;
	size_t sizes[2] = { width, height };
	ThrowIfErrorFftStatus(clfftCreateDefaultPlan(&planHandleTmp, contextAndCommandQueue.cl_dev_context, CLFFT_2D, sizes));
	std::unique_ptr<clfftPlanHandle, Deleter_clfftPlan> planHandle(planHandleTmp);

	ThrowIfErrorFftStatus(clfftSetPlanPrecision(planHandle.get(), CLFFT_SINGLE));

	ThrowIfErrorFftStatus(clfftSetLayout(planHandle.get(), CLFFT_REAL, CLFFT_HERMITIAN_INTERLEAVED));
	//status = clfftSetResultLocation(planHandle, CLFFT_OUTOFPLACE);
	ThrowIfErrorFftStatus(clfftSetResultLocation(planHandle.get(), CLFFT_INPLACE));

	// pitch is given in elements, and an "element" is a complex float (=2 floats) it seems
	//size_t outStrides[2] = { 1, destWidth };// */*sizeof(cl_float)**/ 2
	//status = clfftSetPlanOutStride(planHandle, CLFFT_2D, outStrides);
#if 0
	size_t outStrides[2]; size_t inStrides[2];
	status = clfftGetPlanInStride(planHandle, CLFFT_2D, inStrides);
	status = clfftGetPlanOutStride(planHandle, CLFFT_2D, outStrides);
#endif

	size_t newInStrides[2] = { 1, destWidth * 2 };
	ThrowIfErrorFftStatus(clfftSetPlanInStride(planHandle.get(), CLFFT_2D, newInStrides));
	size_t newOutStrides[2] = { 1, destWidth };
	ThrowIfErrorFftStatus(clfftSetPlanOutStride(planHandle.get(), CLFFT_2D, newOutStrides));

	ThrowIfErrorFftStatus(clfftSetPlanScale(planHandle.get(), CLFFT_FORWARD, scaleFactor));
	//ThrowIfErrorFftStatus(clfftSetPlanScale(planHandle.get(), CLFFT_BACKWARD, scaleFactor));

	ThrowIfErrorFftStatus(clfftBakePlan(planHandle.get(), 1, &contextAndCommandQueue.cl_queue, NULL, NULL));

	size_t tmpBuffersize;
	ThrowIfErrorFftStatus(clfftGetTmpBufSize(planHandle.get(), &tmpBuffersize));
	//cl_mem tmpBufferHandle = 0;
	//tmpBufferHandle = clCreateBuffer(contextAndCommandQueue.cl_dev_context, 0/*CL_MEM_READ_WRITE*/, tmpBuffersize, NULL, &cl_error);
	std::unique_ptr<cl_mem, Deleter_cl_mem> tmpBufferHandle(clCreateBuffer(contextAndCommandQueue.cl_dev_context, CL_MEM_READ_WRITE, tmpBuffersize, NULL, &cl_error));
	if (cl_error != CL_SUCCESS)
	{
		throw std::runtime_error("clCreateBuffer");
	}


	CLFTPLANHANDLEDual clHandle; ZeroMemory(&clHandle, sizeof(clHandle));
	clHandle.internal.clFftplan = planHandle.release();;
	/*clHandle.internal.srcMemoryHandle = srcdata;
	clHandle.internal.outMemoryHandle = destdata;*/
	clHandle.internal.planType = CLFFTHANDLETYPE::_2dRealToComplexPackedFftw;
	clHandle.internal.cl_dev_context = contextAndCommandQueue.cl_dev_context;
	clHandle.internal.cl_queue = contextAndCommandQueue.cl_queue;
	clHandle.internal.tempMemoryHandle = tmpBufferHandle.release();
	clHandle.internal.inoutMemoryHandle = inoutdata.release();
	return clHandle.external;
}

// TODO: use inplace-transform !!!

void clFFT_2d_RealToComplexPackedFftw(const OPENCLFTPLANHANDLE& plan, const FTLib::FourierTransfRealInputData2d& input, const FTLib::FourierTransfComplexOutput2d& output)
{
	const OPENCLFTPLANHANDLEInternal& p = (const OPENCLFTPLANHANDLEInternal&)plan;
	cl_int ret;

	uint32_t destHeight = input.height;
	uint32_t destWidth = input.width / 2 + 1;
	size_t destSize = destHeight * destWidth * sizeof(float) * 2;

	size_t origin[3] = { 0, 0, 0 };
	size_t region[3] = { input.width, input.height, 1 };
	ret = clEnqueueReadImage(p.cl_queue, p.inoutMemoryHandle, CL_TRUE, origin, region, input.pitch, 0, (void*)input.ptrData, 0, NULL, NULL);
	//CL_INVALID_ARG_INDEX

	void* ptr = clEnqueueMapBuffer(p.cl_queue, p.inoutMemoryHandle, CL_TRUE, CL_MAP_WRITE, 0, destSize, 0, NULL, NULL, &ret);

	for (uint32_t y = 0; y < input.height; ++y)
	{
		const float* pSrc = (const float*)(((char*)(input.ptrData)) + y*input.pitch);
		float* pDest = (float*)(((char*)ptr) + y*(destWidth*sizeof(float) * 2));
		memcpy(pDest, pSrc, input.width*sizeof(float));
	}
	//memcpy(ptr, input.ptrData, input.height*input.pitch);

	ret = clEnqueueUnmapMemObject(p.cl_queue, p.inoutMemoryHandle, ptr, 0, NULL, NULL);

	ret = clfftEnqueueTransform(
		p.clFftplan,
		CLFFT_FORWARD,
		1,
		const_cast<cl_command_queue*>(&p.cl_queue),
		0,
		NULL,
		NULL,
		const_cast<cl_mem*>(&p.inoutMemoryHandle),
		NULL,
		p.tempMemoryHandle);

	ret = clFinish(p.cl_queue);

	if (output.width * 2 * sizeof(float) == output.pitch)
	{
		ret = clEnqueueReadBuffer(p.cl_queue, p.inoutMemoryHandle, CL_TRUE, 0, destSize, output.ptrData, 0, NULL, NULL);
	}
	else
	{
		void* ptr = clEnqueueMapBuffer(p.cl_queue, p.inoutMemoryHandle, CL_TRUE, CL_MAP_READ, 0, destSize, 0, NULL, NULL, &ret);
		for (uint32_t y = 0; y < input.height; ++y)
		{
			const float* pSrc = (const float*)(((char*)(ptr)) + y*(destWidth*sizeof(float) * 2));
			float* pDest = (float*)(((char*)output.ptrData) + y*(output.pitch));
			memcpy(pDest, pSrc, output.width * 2 * sizeof(float));
		}

		ret = clEnqueueUnmapMemObject(p.cl_queue, p.inoutMemoryHandle, ptr, 0, NULL, NULL);
	}

	ret = clFinish(p.cl_queue);

	/*
	ret = clEnqueueWriteBuffer(p.cl_queue, p.srcMemoryHandle, CL_TRUE, 0, input.width*input.height*sizeof(cl_float), input.ptrData, 0, NULL, NULL);

	ret = clfftEnqueueTransform(
	p.clFftplan,
	CLFFT_FORWARD,
	1,
	const_cast<cl_command_queue*>(&p.cl_queue),
	0,
	NULL,
	NULL,
	const_cast<cl_mem*>(&p.srcMemoryHandle),
	const_cast<cl_mem*>(&p.outMemoryHandle),
	p.tempMemoryHandle);

	ret = clFinish(p.cl_queue);

	uint32_t destHeight = input.height;
	uint32_t destWidth = input.width / 2 + 1;
	size_t destSize = destHeight * destWidth * sizeof(float) * 2;

	ret = clEnqueueReadBuffer(p.cl_queue, p.outMemoryHandle, CL_TRUE, 0, destSize, output.ptrData, 0, NULL, NULL);
	ret = clFinish(p.cl_queue);*/
}

OPENCLFTPLANHANDLE clFFT_CreatePlan_2dComplexToComplex(uint32_t width, uint32_t height, float scaleFactor)
{
	auto ptrFftHelper = clFftHelper::GetInstance();
	CL_ContextAndCommandQueue ccq = ptrFftHelper->CreateContext();

	cl_int cl_error;
	size_t inoutSize = width * height * 2 * sizeof(float);
	//cl_mem inoutdata = clCreateBuffer(ccq.cl_dev_context, CL_MEM_READ_WRITE, inoutSize, NULL, &cl_error);
	std::unique_ptr<cl_mem, Deleter_cl_mem> inoutdata(clCreateBuffer(ccq.cl_dev_context, CL_MEM_READ_WRITE, inoutSize, NULL, &cl_error));
	if (cl_error != CL_SUCCESS)
	{
		throw std::runtime_error("clCreateBuffer");
	}

	clfftPlanHandle planHandleTmp;
	size_t sizes[2] = { width, height };
	ThrowIfErrorFftStatus(clfftCreateDefaultPlan(&planHandleTmp, ccq.cl_dev_context, CLFFT_2D, sizes));
	std::unique_ptr<clfftPlanHandle, Deleter_clfftPlan> planHandle(planHandleTmp);

	ThrowIfErrorFftStatus(clfftSetPlanPrecision(planHandle.get(), CLFFT_SINGLE));
	ThrowIfErrorFftStatus(clfftSetLayout(planHandle.get(), CLFFT_COMPLEX_INTERLEAVED, CLFFT_COMPLEX_INTERLEAVED));
	ThrowIfErrorFftStatus(clfftSetResultLocation(planHandle.get(), CLFFT_INPLACE));

	/*size_t outStrides[2]; size_t inStrides[2];
	clfftGetPlanInStride(planHandle.get(), CLFFT_2D, inStrides);
	clfftGetPlanOutStride(planHandle.get(), CLFFT_2D, outStrides);*/
	size_t newStrides[2] = { 1, width };
	ThrowIfErrorFftStatus(clfftSetPlanInStride(planHandle.get(), CLFFT_2D, newStrides));
	ThrowIfErrorFftStatus(clfftSetPlanOutStride(planHandle.get(), CLFFT_2D, newStrides));

	ThrowIfErrorFftStatus(clfftSetPlanScale(planHandle.get(), CLFFT_FORWARD, scaleFactor));
	ThrowIfErrorFftStatus(clfftSetPlanScale(planHandle.get(), CLFFT_BACKWARD, scaleFactor));

	ThrowIfErrorFftStatus(clfftBakePlan(planHandle.get(), 1, &ccq.cl_queue, NULL, NULL));

	size_t tmpBuffersize;
	ThrowIfErrorFftStatus(clfftGetTmpBufSize(planHandle.get(), &tmpBuffersize));
	std::unique_ptr<cl_mem, Deleter_cl_mem> tmpBufferHandle(clCreateBuffer(ccq.cl_dev_context, CL_MEM_READ_WRITE, tmpBuffersize, NULL, &cl_error));
	if (cl_error != CL_SUCCESS)
	{
		throw std::runtime_error("clCreateBuffer");
	}

	CLFTPLANHANDLEDual clHandle; ZeroMemory(&clHandle, sizeof(clHandle));
	clHandle.internal.clFftplan = planHandle.release();
	/*clHandle.internal.srcMemoryHandle = srcdata;
	clHandle.internal.outMemoryHandle = destdata;*/
	clHandle.internal.planType = CLFFTHANDLETYPE::_2dComplexToComplex;
	clHandle.internal.cl_dev_context = ccq.cl_dev_context;
	clHandle.internal.cl_queue = ccq.cl_queue;
	clHandle.internal.tempMemoryHandle = tmpBufferHandle.release();
	clHandle.internal.inoutMemoryHandle = inoutdata.release();
	return clHandle.external;
}

void clFFT_2d_ComplexToComplex(const OPENCLFTPLANHANDLE& plan, bool forward, const FTLib::FourierTransfComplexInput2d& input, const FTLib::FourierTransfComplexOutput2d& output)
{
	const OPENCLFTPLANHANDLEInternal& p = (const OPENCLFTPLANHANDLEInternal&)plan;
	cl_int ret;

	size_t bufferOrigin[3] = { 0, 0, 0 };
	size_t* const hostOrigin = bufferOrigin;
	//size_t hostOrigin[3] = { 0, 0, 0 };
	size_t region[3] = { input.width * 2 * sizeof(float), input.height, 1 };
	ret = clEnqueueWriteBufferRect(
		p.cl_queue,
		p.inoutMemoryHandle,
		CL_FALSE,
		bufferOrigin,
		hostOrigin,
		region,
		0,
		0,
		input.pitch,
		0,
		input.ptrData,
		0,
		0,
		0);


	/*if (input.pitch == input.width * 2 * sizeof(float))
	{
	size_t origin[3] = { 0, 0, 0 };
	size_t region[3] = { input.width*2*sizeof(float), input.height, 1 };
	ret = clEnqueueReadImage(p.cl_queue, p.inoutMemoryHandle, CL_TRUE, origin, region, input.pitch, 0, (void*)input.ptrData, 0, NULL, NULL);
	}
	else
	{
	// TODO
	}*/
	/*size_t inoutLineLength = input.width * 2 * sizeof(float);
	size_t inoutBufferSize = inoutLineLength *input.height;
	void* ptr = clEnqueueMapBuffer(p.cl_queue, p.inoutMemoryHandle, CL_TRUE, CL_MAP_WRITE, 0, inoutBufferSize, 0, NULL, NULL, &ret);

	for (uint32_t y = 0; y < input.height; ++y)
	{
	const float* pSrc = (const float*)(((char*)(input.ptrData)) + y*input.pitch);
	float* pDest = (float*)(((char*)ptr) + y*(inoutLineLength));
	memcpy(pDest, pSrc, inoutLineLength);
	}

	ret = clEnqueueUnmapMemObject(p.cl_queue, p.inoutMemoryHandle, ptr, 0, NULL, NULL);*/

	ret = clfftEnqueueTransform(
		p.clFftplan,
		forward == true ? CLFFT_FORWARD : CLFFT_BACKWARD,
		1,
		const_cast<cl_command_queue*>(&p.cl_queue),
		0,
		NULL,
		NULL,
		const_cast<cl_mem*>(&p.inoutMemoryHandle),
		NULL,
		p.tempMemoryHandle);

	//ret = clFinish(p.cl_queue);

	ret = clEnqueueReadBufferRect(
		p.cl_queue,
		p.inoutMemoryHandle,
		CL_FALSE,
		bufferOrigin,
		hostOrigin,
		region,
		0,
		0,
		output.pitch,
		0,
		output.ptrData,
		0,
		0,
		0);

	ret = clFinish(p.cl_queue);
	/*if (output.pitch == output.width * 2 * sizeof(float))
	{
	ret = clEnqueueReadBuffer(p.cl_queue, p.inoutMemoryHandle, CL_FALSE, 0, output.pitch * output.height, output.ptrData, 0, NULL, NULL);
	ret = clFinish(p.cl_queue);
	}*/
	/*ptr = clEnqueueMapBuffer(p.cl_queue, p.inoutMemoryHandle, CL_TRUE, CL_MAP_READ, 0, inoutBufferSize, 0, NULL, NULL, &ret);
	for (uint32_t y = 0; y < input.height; ++y)
	{
	const float* pSrc = (const float*)(((char*)(ptr)) + y*(inoutLineLength));
	float* pDest = (float*)(((char*)output.ptrData) + y*(output.pitch));
	memcpy(pDest, pSrc, inoutLineLength);
	}

	ret = clEnqueueUnmapMemObject(p.cl_queue, p.inoutMemoryHandle, ptr, 0, NULL, NULL);*/
}

OPENCLFTPLANHANDLE clFFT_CreatePlan_2dComplexHermetianToReal(uint32_t width, uint32_t height, float scaleFactor)
{
	auto ptrFftHelper = clFftHelper::GetInstance();
	CL_ContextAndCommandQueue ccq = ptrFftHelper->CreateContext();

	cl_int cl_error;
	size_t inoutSize = (width / 2 + 1) * height * 2 * sizeof(float);
	//cl_mem inoutdata = clCreateBuffer(ccq.cl_dev_context, CL_MEM_READ_WRITE, inoutSize, NULL, &cl_error);
	std::unique_ptr<cl_mem, Deleter_cl_mem> inoutdata(clCreateBuffer(ccq.cl_dev_context, CL_MEM_READ_WRITE, inoutSize, NULL, &cl_error));
	if (cl_error != CL_SUCCESS)
	{
		throw std::runtime_error("clCreateBuffer");
	}

	clfftPlanHandle planHandleTmp;
	size_t sizes[2] = { width, height };
	ThrowIfErrorFftStatus(clfftCreateDefaultPlan(&planHandleTmp, ccq.cl_dev_context, CLFFT_2D, sizes));
	std::unique_ptr<clfftPlanHandle, Deleter_clfftPlan> planHandle(planHandleTmp);

	ThrowIfErrorFftStatus(clfftSetPlanPrecision(planHandle.get(), CLFFT_SINGLE));
	ThrowIfErrorFftStatus(clfftSetLayout(planHandle.get(), CLFFT_HERMITIAN_INTERLEAVED, CLFFT_REAL));
	ThrowIfErrorFftStatus(clfftSetResultLocation(planHandle.get(), CLFFT_INPLACE));

	size_t outStrides[2]; size_t inStrides[2];
	clfftGetPlanInStride(planHandle.get(), CLFFT_2D, inStrides);
	clfftGetPlanOutStride(planHandle.get(), CLFFT_2D, outStrides);
	size_t newInStrides[2] = { 1, (width / 2 + 1)  /** sizeof(float)*/ };
	ThrowIfErrorFftStatus(clfftSetPlanInStride(planHandle.get(), CLFFT_2D, newInStrides));
	size_t newOutStrides[2] = { 1, width /** sizeof(float)*/ };
	ThrowIfErrorFftStatus(clfftSetPlanOutStride(planHandle.get(), CLFFT_2D, newOutStrides));

	ThrowIfErrorFftStatus(clfftSetPlanScale(planHandle.get(), CLFFT_FORWARD, scaleFactor));
	ThrowIfErrorFftStatus(clfftSetPlanScale(planHandle.get(), CLFFT_BACKWARD, scaleFactor));

	ThrowIfErrorFftStatus(clfftBakePlan(planHandle.get(), 1, &ccq.cl_queue, NULL, NULL));

	size_t tmpBuffersize;
	ThrowIfErrorFftStatus(clfftGetTmpBufSize(planHandle.get(), &tmpBuffersize));
	std::unique_ptr<cl_mem, Deleter_cl_mem> tmpBufferHandle(clCreateBuffer(ccq.cl_dev_context, CL_MEM_READ_WRITE, tmpBuffersize, NULL, &cl_error));
	if (cl_error != CL_SUCCESS)
	{
		throw std::runtime_error("clCreateBuffer");
	}

	CLFTPLANHANDLEDual clHandle; ZeroMemory(&clHandle, sizeof(clHandle));
	clHandle.internal.clFftplan = planHandle.release();
	/*clHandle.internal.srcMemoryHandle = srcdata;
	clHandle.internal.outMemoryHandle = destdata;*/
	clHandle.internal.planType = CLFFTHANDLETYPE::_2dComplexHermetianToReal;
	clHandle.internal.cl_dev_context = ccq.cl_dev_context;
	clHandle.internal.cl_queue = ccq.cl_queue;
	clHandle.internal.tempMemoryHandle = tmpBufferHandle.release();
	clHandle.internal.inoutMemoryHandle = inoutdata.release();
	return clHandle.external;
}

void clFFT_2d_ComplexHermetianToReal(const OPENCLFTPLANHANDLE& plan, const FTLib::FourierTransfComplexInput2d& input, const FTLib::FourierTransfRealOutputData2d& output)
{
	const OPENCLFTPLANHANDLEInternal& p = (const OPENCLFTPLANHANDLEInternal&)plan;
	cl_int ret;

	size_t bufferOrigin[3] = { 0, 0, 0 };
	size_t* const hostOrigin = bufferOrigin;
	//size_t hostOrigin[3] = { 0, 0, 0 };
	size_t region[3] = { (input.width / 2 + 1) * 2 * sizeof(float), input.height, 1 };
	ret = clEnqueueWriteBufferRect(
		p.cl_queue,
		p.inoutMemoryHandle,
		CL_FALSE,
		bufferOrigin,
		hostOrigin,
		region,
		0,
		0,
		input.pitch,
		0,
		input.ptrData,
		0,
		0,
		0);

	//size_t destSize = (input.width / 2 + 1) * 2 * sizeof(float)*input.height;
	//void* ptr = clEnqueueMapBuffer(p.cl_queue, p.inoutMemoryHandle, CL_TRUE, CL_MAP_WRITE, 0, destSize, 0, NULL, NULL, &ret);
	//ret = clEnqueueUnmapMemObject(p.cl_queue, p.inoutMemoryHandle, ptr, 0, NULL, NULL);

	ret = clfftEnqueueTransform(
		p.clFftplan,
		CLFFT_BACKWARD,
		1,
		const_cast<cl_command_queue*>(&p.cl_queue),
		0,
		NULL,
		NULL,
		const_cast<cl_mem*>(&p.inoutMemoryHandle),
		NULL,
		p.tempMemoryHandle);

	/*ptr = clEnqueueMapBuffer(p.cl_queue, p.inoutMemoryHandle, CL_TRUE, CL_MAP_WRITE, 0, destSize, 0, NULL, NULL, &ret);
	ret = clEnqueueUnmapMemObject(p.cl_queue, p.inoutMemoryHandle, ptr, 0, NULL, NULL);*/

	size_t regionOut[3] = { (input.width) *  sizeof(float), input.height, 1 };
	ret = clEnqueueReadBufferRect(
		p.cl_queue,
		p.inoutMemoryHandle,
		CL_FALSE,
		bufferOrigin,
		hostOrigin,
		/*region*/regionOut,
		0,
		0,
		output.pitch,
		0,
		output.ptrData,
		0,
		0,
		0);

	ret = clFinish(p.cl_queue);
}

void clFFT_DeletePlan(OPENCLFTPLANHANDLE& planHandle)
{
	OPENCLFTPLANHANDLEInternal& p = (OPENCLFTPLANHANDLEInternal&)planHandle;
	cl_int r;
	clfftStatus status;

	if (p.clFftplan != 0)
	{
		status = clfftDestroyPlan(&p.clFftplan);
	}

	/*if (p.srcMemoryHandle != NULL)
	{
	r = clReleaseMemObject(p.srcMemoryHandle);
	}

	if (p.outMemoryHandle != NULL)
	{
	r = clReleaseMemObject(p.outMemoryHandle);
	}*/
	if (p.inoutMemoryHandle != NULL)
	{
		r = clReleaseMemObject(p.inoutMemoryHandle);
	}

	if (p.tempMemoryHandle != NULL)
	{
		r = clReleaseMemObject(p.tempMemoryHandle);
	}

	if (p.cl_queue != NULL)
	{
		r = clReleaseCommandQueue(p.cl_queue);
	}

	if (p.cl_dev_context != NULL)
	{
		r = clReleaseContext(p.cl_dev_context);
	}
}

void clFFT_TearDown()
{
	clfftTeardown();
}